package fr.cephb.joperon.core.iterator;

import com.sleepycat.db.Cursor;
import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.OperationStatus;

import fr.cephb.berkeley.DBUtils;
import fr.cephb.util.Pair;

/**
 * Iterate over a pair(key,value) of DatabaseEntry
 * @author lindenb
 *
 */
public class DatabaseEntriesIterator
	extends AbstractIterator<Pair<DatabaseEntry, DatabaseEntry>>
	{

	protected Cursor cursor=null;
	/**
	 * Constructor
	 * @param cursor current cursor
	 */
	public DatabaseEntriesIterator(Cursor cursor)
		{
		this(cursor,new DatabaseEntry(),new DatabaseEntry());
		}
	
	/**
	 * Constructor, we specify the initial value
	 * of the key/value. This will be used in the derived class see {@link KeyRangeIterator}
	 * @param cursor current cursor
	 * @param 
	 */
	public DatabaseEntriesIterator(Cursor cursor,DatabaseEntry key,DatabaseEntry value)
		{
		this.cursor=cursor;
		if(key==null) key=new DatabaseEntry();
		if(value==null) value=new DatabaseEntry();
		this._nextValue=new Pair<DatabaseEntry, DatabaseEntry>(
			key,
			value
			);
		}
	
	@Override
	public void remove()
		{
		try {
			getCursor().delete();
			}
		catch (DatabaseException e) {
			throw new RuntimeException(e);
			}
		}
	
	@Override
	public void close()
		{
		if(this.cursor!=null)
			{
			DBUtils.safeClose(this.cursor);
			this.cursor=null;
			}
		super.close();
		}
	
	@Override
	public boolean hasNext() {
		if(this.eofMet) return false;
		if(this._hasNextTested) return this._hasNext;
		this._hasNextTested=true;
		this._hasNext=false;
		if(this.eofMet) return false;
		try {
			//move the cursor to the next position
			OperationStatus status= moveCursor();
			
			//move was a success and we validate this position
			if(!(status==OperationStatus.SUCCESS &&
				validateCursor()))
				{
				this._hasNext=false;
				this.eofMet=true;
				close();
				}
			else
				{
				this._hasNext=true;
				}
			} 
		catch (DatabaseException e)
			{
			this.close();
			this.eofMet=true;
			throw new RuntimeException(e);
			}
		return this._hasNext;
		}
	
	/** get the Cursor */
	protected Cursor getCursor()
		{
		return this.cursor;
		}
	
	/** move the cursor to its next position */
	protected OperationStatus moveCursor() throws DatabaseException
		{
		return getCursor().getNext(
				_nextValue.first(),
				_nextValue.second(),
				LockMode.DEFAULT
				);
		}
	
	/** gives this iterator a chance to say: ok I've succefully
	 * moved the cursor, now should I stop the scanning ?
	 * @return true if this Iteraror 'hasNext'
	 * @throws DatabaseException
	 */
	protected boolean validateCursor() throws DatabaseException
		{
		return true;
		}
}
